package com.sl.au.service;

import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.util.Date;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;
import java.util.SortedMap;
import java.util.TreeMap;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

import org.apache.poi.xwpf.usermodel.XWPFDocument;
import org.apache.poi.xwpf.usermodel.XWPFParagraph;
import org.apache.poi.xwpf.usermodel.XWPFRun;
import org.apache.poi.xwpf.usermodel.XWPFTable;
import org.apache.poi.xwpf.usermodel.XWPFTableCell;
import org.apache.poi.xwpf.usermodel.XWPFTableRow;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Service;
import org.springframework.util.ResourceUtils;

import com.sl.utils.DataConverter;



/**
 * word格式相关操作
 * 
 * @author Neil
 *
 */
@Service
public class DocService {

	private static String uploadLocation;

	@Autowired
	private DocService(@Value("${storage.location}") String dir) {
		DocService.uploadLocation = dir;
	}


	/**
	 * 读取模板文件，填充数据生成WORD文档,返回生成文件的全路径 
	 * 
	 * @param inFile模板文件
	 *            .docx短文件名称//保存在resources/word/下的模板文件
	 * @param data数据源
	 * @param outName输出.docx短文件名称//保存到upload目录下
	 */
	public static String genTemplate(String inFile, Map<String, Object> data, String outName) {
		String inpath = null, outpath = null;
		InputStream input;
		String uploadPath = DocService.uploadLocation + "/";// "c:/upload/";
		@SuppressWarnings("unchecked")
		Map<String,String> textData=((Map<String,String>)data.get("textData"));
		@SuppressWarnings("unchecked")
		Map<String,LinkedList<List<String>>> tableData=(Map<String,LinkedList<List<String>>>)data.get("tablesData");
		try {
			//inpath = ResourceUtils.getURL("classpath:word/").getPath() + inFile;
			input = ResourceUtils.getURL("classpath:word/"+ inFile).openStream();
			outpath = uploadPath + outName + DataConverter.toTimeCode(new Date()) + ".docx";
			XWPFDocument doc = DocService.open(input);

			
			DocService.processForeignTemplateTableDynamic(doc,tableData);
			for (XWPFParagraph p : doc.getParagraphs()) {
				replace$(p, textData);
			}
			for (XWPFTable tbl : doc.getTables()) {
				for (XWPFTableRow row : tbl.getRows()) {
					for (XWPFTableCell cell : row.getTableCells()) {
						for (XWPFParagraph p : cell.getParagraphs()) {
							replace$(p, textData);
						}
					}
				}
			}

			DocService.write(doc, outpath);

		} catch (Exception e) {
			System.out.println("处理生成文档出错！" + e.getMessage());
		}

		return outpath;
	}

	/**
	 * 替换文本中的${}
	 * 
	 * @param p
	 * @param data
	 */
	private static void replace$(XWPFParagraph p, Map<String, String> data) {
		String pText = p.getText(); // complete paragraph as string
		if (pText.contains("${")) { // if paragraph does not include our
									// pattern, ignore
			TreeMap<Integer, XWPFRun> posRuns = getPosToRuns(p);
			Pattern pat = Pattern.compile("\\$\\{(.+?)\\}");
			Matcher m = pat.matcher(pText);
			while (m.find()) { // for all patterns in the paragraph
				String g = m.group(1); // extract key start and end pos
				int s = m.start(1);
				int e = m.end(1);
				String key = g;
				String x = data.get(key);
				if (x == null)
					x = "";
				SortedMap<Integer, XWPFRun> range = posRuns.subMap(s - 2, true, e + 1, true); // get
																								// runs
																								// which
																								// contain
																								// the
																								// pattern
				boolean found1 = false; // found $
				boolean found2 = false; // found {
				boolean found3 = false; // found }
				XWPFRun prevRun = null; // previous run handled in the loop
				XWPFRun found2Run = null; // run in which { was found
				int found2Pos = -1; // pos of { within above run
				for (XWPFRun r : range.values()) {
					if (r == prevRun)
						continue; // this run has already been handled
					if (found3)
						break; // done working on current key pattern
					prevRun = r;
					for (int k = 0;; k++) { // iterate over texts of run r
						if (found3)
							break;
						String txt = null;
						try {
							txt = r.getText(k); // note: should return null, but
												// throws exception if the text
												// does not exist
						} catch (Exception ex) {

						}
						if (txt == null)
							break; // no more texts in the run, exit loop
						if (txt.contains("$") && !found1) { // found $, replace
															// it with value
															// from data map
							txt = txt.replaceFirst("\\$", x);
							found1 = true;
						}
						if (txt.contains("{") && !found2 && found1) {
							found2Run = r; // found { replace it with empty
											// string and remember location
							found2Pos = txt.indexOf('{');
							txt = txt.replaceFirst("\\{", "");
							found2 = true;
						}
						if (found1 && found2 && !found3) { // find } and set all
															// chars between {
															// and } to blank
							if (txt.contains("}")) {
								if (r == found2Run) { // complete pattern was
														// within a single run
									txt = txt.substring(0, found2Pos) + txt.substring(txt.indexOf('}'));
								} else // pattern spread across multiple runs
									txt = txt.substring(txt.indexOf('}'));
							} else if (r == found2Run) // same run as { but no
														// }, remove all text
														// starting at {
								txt = txt.substring(0, found2Pos);
							else
								txt = ""; // run between { and }, set text to
											// blank
						}
						if (txt.contains("}") && !found3) {
							txt = txt.replaceFirst("\\}", "");
							found3 = true;
						}
						r.setText(txt, k);
					}
				}
			}
			// System.out.println(p.getText());

		}

	}

	/**
	 * 返回段落中所有的RUN
	 * 
	 * @param paragraph
	 * @return
	 */
	private static TreeMap<Integer, XWPFRun> getPosToRuns(XWPFParagraph paragraph) {
		int pos = 0;
		TreeMap<Integer, XWPFRun> map = new TreeMap<Integer, XWPFRun>();
		for (XWPFRun run : paragraph.getRuns()) {
			String runText = run.text();
			if (runText != null && runText.length() > 0) {
				for (int i = 0; i < runText.length(); i++) {
					map.put(pos + i, run);
				}
				pos += runText.length();
			}

		}
		return map;
	}

	/**
	 * 打开指定DOCX文件，返回文档对象
	 * 
	 * @param filePath要打开的文档路径
	 * @return
	 */
	public static XWPFDocument open(InputStream input) {
		XWPFDocument doc = null;
		//try {
			try {
				//doc = new XWPFDocument(OPCPackage.open(filePath));
				doc=new XWPFDocument(input);
			} catch ( IOException e) {
				// TODO Auto-generated catch block
				System.out.println("文件：" + input.toString() + "打开失败！");
				e.printStackTrace();
				System.out.println("文件：打开失败！ 错误信息结束！");
			}
		//}
	/*	catch (Exception e) {

			System.out.println("文件：" + filePath + "打开失败！");

		}*/

		return doc;
	}

	/**
	 * 将指定doc文档写入到指定文件中
	 * 
	 * @param doc
	 * @param filename要写入的全新文档
	 * @return
	 */
	public static String write(XWPFDocument doc, String filename) {
		if (null == doc)
			return null;
		FileOutputStream fos = null;
		try {
			fos = new FileOutputStream(new File(filename));
			doc.write(fos);
			fos.close();
		} catch (Exception e) {
			System.out.println("写入文件：" + filename + "失败");
		}
		return filename;

	}

	/**
	 * 向文档中插入一张表格
	 * 
	 * @param docx
	 * @param data
	 *            表格数据存储在一个LinkedList
	 */
	private static void processTemplateTableDynamic(XWPFDocument docx, Map<String, LinkedList<List<String>>> data) {
		String cell1 = "";
		for (XWPFTable t : docx.getTables()) {
			cell1 = t.getRow(0).getCell(0).getText();
			if (cell1.contains("${table:")) {
				int k = cell1.lastIndexOf("}");
				int j = cell1.lastIndexOf(":");
				String listName = cell1.substring(j+1, k);
				LinkedList<List<String>> rows = data.get(listName);
				if (null == rows)
					continue;
				// XmlCursor cursor =t.getRow(0).getCTP().newCursor();
				XWPFTableRow tr;
				XWPFTableCell cell;
				for (List<String> row : rows) {
					tr = t.createRow();
					int pos=0;
					for (String cellData : row) {
						tr.getCell(pos++).setText(cellData);
						
					}
				} // for row
			} // if contain table data
		} // tables for
	}
	/**
	 * 向外贸文档中插入一张表格
	 * 
	 * @param docx
	 * @param data
	 *            表格数据存储在一个LinkedList
	 */
	private static void processForeignTemplateTableDynamic(XWPFDocument docx, Map<String, LinkedList<List<String>>> data) {
		String cell1 = "";
		for (XWPFTable t : docx.getTables()) {
			cell1 = t.getRow(0).getCell(0).getText();
			if (cell1.contains("${table:")) {
				int k = cell1.lastIndexOf("}");
				int j = cell1.lastIndexOf(":");
				String listName = cell1.substring(j+1, k);
				LinkedList<List<String>> rows = data.get(listName);
				if (null == rows)
					continue;
				// XmlCursor cursor =t.getRow(0).getCTP().newCursor();
				XWPFTableRow tr;
				XWPFTableCell cell;
				for (List<String> row : rows) {
					tr = t.createRow();
					int pos=0;
					for (String cellData : row) {
						tr.getCell(pos++).setText(cellData);
					}
				} // for row
			} // if contain table data

		} // tables for
	}

}
